/**
 *
 *  Util.Plot.TimeSeriesComponent, V0.7
 *
 *  @author Michael Liljenstam
 *
 */

package SSF.Util.Plot;
import java.awt.*;
import java.io.*;


/**
 *  Provides a programmatic API to the SSF.Util.Plot package.
 *  An AWT Container that holds the plot panels and provides an API to 
 *  the plots. See constructors for more details.
 *  
 *  
 *  
 *
 *
 *  <br><br>
 *
 *  $Id: TimeSeriesComponent.java,v 1.5 2003/02/13 19:34:00 mili Exp $
 */

public class TimeSeriesComponent extends java.awt.Container {


    ///////////////////////////////////////////////////////////////////
    ////                         constructors                      ////

    /**
     *  Constructor common for this class and subclasses.
     *  If used to construct the component it will create it without an 
     *  parent (encapsulating) window. <br>
     *
     *  Creates <code>LivePlotter</code>s and then calls 
     *  <code>plotLayout</code> to display them.
     *  
     *  @param  numDatasets     Number of plots, i.e. plot panels.
     *  @param  winYsize        Total component Y size (pixels). Can be set 
     *                          to 0 to get a default.
     */
    public TimeSeriesComponent(int numDatasets, int winYsize) {

	// inits
	_numDatasets = numDatasets;


	// create the plots (panels)
	_plots = new LivePlotter[_numDatasets];

	for (int i=0; i<_numDatasets; i++) {
	    _plots[i] = new LivePlotter();
	}

	plotLayout(winYsize / numDatasets);

	// set owner
	for (int i=0; i<_numDatasets; i++) {
	    // set owning appl window for live plotters
	    _plots[i].setOwnerAppl(this);
	}

    }


    /**
     *  Constructs a component object with an encapsulating window (Frame).
     *  First calls simpler constructor and then creates a Frame for the 
     *  plot(s).
     *  
     *  @param  numDatasets     Number of plots, i.e. plot panels.
     *  @param  windowTitle     Window title text.
     *  @param  winYsize        Total component Y size (pixels). Can be set 
     *                          to 0 to get a default.
     */
    public TimeSeriesComponent(int numDatasets, 
			       String windowTitle, int winYsize)
	throws Exception {


	// inits
	this(numDatasets, winYsize);

	// create the window (frame)
	try {
	    setCallbackWindow(new TimeSeriesWindow(windowTitle, this));
	} catch (Exception e) {
	    System.err.println("Couldn't create window!");
	    System.err.println(e);
	    throw e;
	}
    }


    ///////////////////////////////////////////////////////////////////
    ////                         public methods                    ////

    /**
     *  Sets a handle to the window to the parent window/application 
     *  for callbacks.
     *  @param  win     Parent window.
     */
    public void setCallbackWindow(Object win) {
	if (isCallbackImplemented(win)) {
	    _window = (TimeSeriesWindowCallback) win;
	}
    }

    /**
     *  Gets the handle to the parent window/application.
     *  @return  Reference to the callback interface for the parent window.
     */
    public TimeSeriesWindowCallback getCallbackWindow() {
	return _window;
    }


    /** 
     *  Sets title of plot. 
     *  Also repaints the plot.
     *  @param  title     Plot title text.
     */
    public void setTitle(String title) {
	if (_plots != null) {
	    _plots[0].setTitle(title);
	    _plots[0].repaint();
	}
    }

    /** 
     *  Sets time bin size of all plots.
     *  @param  binSize   Time-bin width in x-scale units.
     */
    public void setBinSize(float binSize) {
	if (_plots != null) {
	    for (int i=0; i<_numDatasets; i++) {
		_plots[i].setBinSize(binSize);
	    }
	}
    }

    /** 
     *  Sets time window size of all plots for a moving window plot.
     *  @param  winSize   Moving window size in x-scale units.
     */
    public void setTimeWindowSize(float winSize) {
	if (_plots != null) {
	    for (int i=0; i<_numDatasets; i++) {
		_plots[i].setTimeWindowSize(winSize);
	    }
	}
    }

    /** 
     *  Sets number of steps before rescaling for all plots.
     *  For sparser rescaling.
     *  @param  rescaleStep     Number of points plotted before rescaling.
     */
    public void setRescaleStep(int rescaleStep) {
	if (_plots != null) {
	    for (int i=0; i<_numDatasets; i++) {
		_plots[i].setRescaleStep(rescaleStep);
	    }
	}
    }


    /** 
     *  Gets handle on a plot. 
     *  @param   index     Index of plot, from 0.
     *  @return  Reference to one of the plots in the component. Returns 
     *           null if there are no plots.
     **/
    public LivePlotter getPlot(int index) {
	if (_plots != null) {
	    return _plots[index];
	} else {
	    //	    System.err.println("here");
	    return null;
	}
    }


    /** 
     *  Adds one data point to one of the plots.
     *  This is the preferred method for adding data points to the plots.
     *  
     *  @param  plotIndex      Index of plot panel, starting from 0.
     *  @param  time           x coordinate.
     *  @param  value          y coordinate.
     */
    public void addPoint(int plotIndex, double time, float value) {
	if (_plots != null) {
	    _plots[plotIndex].addSinglePoint(time, value);
	}
    }


    /** 
     *  Adds one round of data points to all plots for a "live" plot. 
     *  The data array parameter should hold a row for each plot (panel) and 
     *  a column for each data point plotted so far and including the new one. 
     *  The plot keeps a counter by itself that progresses through the 
     *  columns of the data array. <br>
     *  <i>This method will be deprecated. Use</i> <code>addPoint</code> 
     *  <i>or</i> <code>addAllPoints</code> <i>methods instead.</i>
     *  
     *  @param  dataArray     Data to be plotted. 
     */
    public void addPoints(Object[][] dataArray) {
	if (_plots != null) {
	    for (int i=0; i<_numDatasets; i++) {
		_plots[i].addPoint(dataArray, i);
	    }
	}
    }

    /** 
     *  Add all points for "static" plot.
     *  Plots all the data in the data array parameter at once.
     *  @param  dataArray     Data to be plotted. One row for each plot and 
     *                        one column for each data point.
     */
    public void addAllPoints(Object[][] dataArray) {
	if (_plots != null) {
	    for (int i=0; i<_numDatasets; i++) {
		_plots[i].addAllPoints(dataArray, i);
	    }
	}
    }

    /** 
     *  Resets all plots.
     */
    public void resetAll() {
	if (_plots != null) {
	    for (int i=0; i<_numDatasets; i++) {
		_plots[i].resetPlot();
	    }
	}
    }

    /**
     *  Starts all plots.
     */
    public void startAll() {
	if (_plots != null) {
	    for (int i=0; i<_plots.length; i++) {
		_plots[i].startPlot();
	    }
	}
    }

    /**
     *  Stops all plots.
     */
    public void stopAll() {
	// set plots to stopped
	if (_plots != null) {
	    for (int i=0; i<_plots.length; i++) {
		_plots[i].stopPlot();
	    }
	}
    }

    /**
     *  Continues all plots.
     */
    public void continueAll() {
	if (_plots != null) {
	    for (int i=0; i<_plots.length; i++) {
		_plots[i].continuePlot();
	    }
	}
    }

    /**
     *  Fills all plots.
     */
    public void fillAll() {
	if (_plots != null) {
	    for (int i=0; i<_numDatasets; i++) {
		_plots[i].fillPlot();
	    }
	}
    }	

    /**
     *  Prints all plots.
     */
    public void printAll(Graphics printgraphics) {
	if (_plots != null) {
	    for (int i=0; i<_plots.length; i++)
		_plots[i].printAll(printgraphics);
	}
    }

    /**
     *  Exports all plots in EPS format.
     */
    public void exportAll(FileOutputStream fout) {
	if (_plots != null) {
	    for (int i=0; i<_plots.length; i++)
		_plots[i].export(fout);
	}
    }


    /**
     *  Signals to the plotting component that its window has been closed 
     *  (deallocated). The plotting component will continue to exist 
     *  (presumably created by someone else) but will stop "responding" to 
     *  method calls since the plot panels are dereferenced.
     */
    public void signalPlotWindowClosed() {
	setCallbackWindow(null); // remove callback window ref
	_plots = null; // remove references to plot panels - so that nothing 
		       // happens if plot methods are called.
    }

    // new 010726

    /**
     *  Called from from one plot when it is rescaled (zoomed) to rescale
     *  (rezoom) all other plots to the same time-scale.
     *  @param  lowx     Left x in x-axis units.
     *  @param  highx    Right x in x-axis units.
     *  @param  caller   Reference to calling plot (if plot) or other object.
     */
    public void rezoom(double lowx, double highx, Object caller)
    {
	for (int i=0; i<_plots.length; i++) {
	    if (_plots[i] != caller)
		_plots[i].zoomX(lowx, highx);
	}
    }

    /**
     *  Adds a legend to one of the plots. Only one legend per plot allowed.
     *  Plotted in lower left-hand corner instead of standard ptplot 
     *  position.
     *  @param  dataSet  Index to plot, from 0.
     *  @param  legen    Legend text.
     */
    public void addLegend(int dataSet, String legend)
    {
	_plots[dataSet].addLegend(0, legend);
    }


    ////  methods delegated to the window  //////////////////////////

    /**
     *  Signals to the parent that the plot has finished.
     */
    public void signalEndOfPlot()
    {
	if (_window != null) {
	    _window.signalEndOfPlot();
	}
    }


    /**
     *  Checks if callback interface is implemented.
     *  @param  win    Perform check on this GUI object. Presumably a parent 
     *                 window.
     *  @return True if <code>win</code> implements the callback interface, 
     *          otherwise false.
     */
    public boolean isCallbackImplemented(Object win)
    {
	boolean impl = false;
	if (win != null) {
	    Object[] ifs = win.getClass().getInterfaces();
	    for (int i=0; i<ifs.length; i++) {
		if (ifs[i].getClass().getName().equals("SSF.Util.Plot.TimeSeriesWindowCallback")) {
		    impl = true;
		}
	    }
	}
	return impl;
    }


    ///////////////////////////////////////////////////////////////////
    ////                         protected methods                 ////

    /**
     *  Layout plot panels in the container.
     *  @param  plotHeights   Height of each plot in pixels. Set to 0 for 
     *                        default.
     */
    protected void plotLayout(int plotHeights) {

	//        add("Center", plot);
        // FIXME: This should not be hardwired in here.

	if (plotHeights > 0) { //plot heights given as argument?
	    setSize(500, plotHeights * _plots.length);
	} else {
	    // set defaults
	    if (_plots.length < 4) { // small number of datasets (larger plot)
		setSize(500, normalPlotHeight * _plots.length);
		smallPlots = false;
	    } else {		// otherwise make smaller plots
		setSize(500, smallPlotHeight * _plots.length);
		smallPlots = true;

		System.err.println("y size : " + smallPlotHeight * _plots.length);

	    }
	}

        // Layout the plots
        GridBagLayout gridbag = new GridBagLayout();
        GridBagConstraints c = new GridBagConstraints();
        setLayout(gridbag);

        c.gridx = 0;

	for (int i=0; i<_plots.length; i++) {
	    c.gridy = i;
	    
	    c.weightx = 1.0;
	    c.weighty = 1.0;
	    c.fill = GridBagConstraints.BOTH;

	    if (smallPlots &&
		i == (_plots.length - 1)) // last one of small plots?
		c.ipady = 15;	// add 30 (15*2) pixels to last plot for 
				// x-axis scale & label

	    gridbag.setConstraints(_plots[i], c);

	    add(_plots[i]);

	    // disable appropriate titles and x-axis labels
	    if (i > 0 || smallPlots)
		_plots[i]._disableTitle = true;
	    if (i < (_plots.length - 1))
		_plots[i]._disableXscale = true;

	    // set owner window

	    _plots[i].setOwner(this);
	}
    }


    ///////////////////////////////////////////////////////////////////
    ////                         public variables                  ////

    /** Flag if we're doing many small plots in the window. In that case, 
	we'll try to same some "real estate" */
    public boolean                  smallPlots = false;


    ///////////////////////////////////////////////////////////////////
    ////                         constants                         ////

    /**
     *  Default plot height [#pixels] for small number of plots < 4.
     */
    private final int     normalPlotHeight = 250;


    /**
     *  Small plot height [#pixels] for larger number of plots > 3.
     */
    private final int     smallPlotHeight = 100;


    ///////////////////////////////////////////////////////////////////
    ////                         protected variables               ////

    /** Plot panels within the window */
    public LivePlotter _plots[] = null;


    ///////////////////////////////////////////////////////////////////
    ////                         private variables                 ////


    /** Number of plots within the window */
    int _numDatasets = 0;

    /** Callback Window (Frame) */
    TimeSeriesWindowCallback _window = null;
}



/*=                                                                      =*/
/*=  Copyright (c) 1997--2000  SSF Research Network                      =*/
/*=                                                                      =*/
/*=  SSFNet is open source software, distributed under the GNU General   =*/
/*=  Public License.  See the file COPYING in the 'doc' subdirectory of  =*/
/*=  the SSFNet distribution, or http://www.fsf.org/copyleft/gpl.html    =*/
/*=                                                                      =*/
